#define ZCORE_SOURCE
#include "Random.hpp"
#include "Math.hpp"

namespace zzz{
//RandomLCG RandomLCG::rng;

const int RandomLCG::a = 16807;
const int RandomLCG::m = 2147483647;
const int RandomLCG::q = 127773;
const int RandomLCG::r = 2836;

RandomLCG::RandomLCG(const zuint seed)
: idum_(0)
{
    RandomLCG::Seed(seed);
    ZCHECK(idum_!=0);
}

RandomLCG::RandomLCG(const RandomLCG &rng)
{
    idum_ = rng.idum_;
}

zuint RandomLCG::Rand()
{
// Based on Numerical Recipes in C
// and FreeBSD implementation of libkern/random.c
//   http://minnie.cs.adfa.edu.au/FreeBSD-srctree/newsrc/libkern/random.c.html
//   http://www.scd.ucar.edu/zine/96/spring/articles/3.random-6.html
//   http://www.mactech.com/articles/mactech/Vol.08/08.03/RandomNumbers/
//   http://wad.www.media.mit.edu/people/wad/mas864/psrc/random.c.txt
//   http://prog.cz/swag/swag/numbers/0081.htm
//   http://www.dcs.gla.ac.uk/mail-www/glasgow-haskell-users/msg00158.html

    int t = a*(idum_%q) - r*(idum_/q);
    if (t <= 0) t += m;
    idum_ = t;
    return idum_;
}

void RandomLCG::Seed(const zuint seed)
{
    if (seed) idum_ = seed;
    else idum_ = 25101974;
    ZCHECK(idum_!=0);
}

void RandomLCG::SeedFromTime()
{
  Seed(TimeSeed());
}
zuint RandomLCG::Max()  const { return m-1;   }

//////////////////////////////////////////////////////////////////////

RandomLFSRMix RandomLFSRMix::rng;

RandomLFSRMix::RandomLFSRMix(const zuint seed1,const zuint seed2,const zuint seed3)
{
    Seed(seed1,seed2,seed3);
}

RandomLFSRMix::RandomLFSRMix(const RandomLFSRMix &rng)
{
    Seed(rng.regA_,rng.regB_,rng.regC_);
}

zuint RandomLFSRMix::Rand()
{
    zuint result = 0;
    for (int j=0; j<32; j++)
    {
        regA_ = ((((regA_ >> 31) ^ (regA_ >> 6) ^ (regA_ >> 4) ^ (regA_ >> 2) ^ (regA_ << 1) ^ regA_) & 0x00000001) << 31) | (regA_ >> 1);
        regB_ = ((((regB_ >> 30) ^ (regB_ >> 2)) & 0x00000001) << 30) | (regB_ >> 1);
        regC_ = ((((regC_ >> 28) ^ (regC_ >> 1)) & 0x00000001) << 28) | (regC_ >> 1);
        result = (result << 1) ^ (((regA_ & regB_) | (~regA_ & regC_)) & 0x00000001);
    }
    return result;
}

void RandomLFSRMix::Seed(const zuint seed1,const zuint seed2,const zuint seed3)
{
    regA_ = seed1;
    regB_ = seed2;
    regC_ = seed3;
}

void RandomLFSRMix::SeedFromTime()
{
  Seed(TimeSeed()+regA_,TimeSeed()+regB_,TimeSeed()+regC_);
}

zuint RandomLFSRMix::Max() const { return MAX_UINT; }
}
